#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Mikan ビュー パッケージ

Mikan Python Web Framework
"""

__author__ = "K.Sonohara"
__status__ = "develop"
__version__ = "0.0.0_28"
__date__    = "2013/08/18"
__license__ = 'LGPL'

# ------------------------------------------------------------
## インポート
# ------------------------------------------------------------
# Python
import sys
import os
import ConfigParser
import json
import mimetypes
import traceback

# XML
try:
	import libxml2
	import libxslt
except:
	pass

# ESiPC
from esipc.xml import str_node
from esipc import ESiPCProperty

# Mikan
from mikan.core import AbstractMikanPerform, MIKAN_HTTP_HEADER
from mikan.viewlet import MikanHeaderButtonItem, MikanHTMLViewlet, MikanHeadViewlet,\
	MikanMetaContentTypeViewlet, MikanMetaContentLanguageViewlet, MikanMetaViewlet,\
	MikanMetaContentStyleTypeViewlet, MikanTitleViewlet, MikanMetaKeywordsViewlet,\
	MikanMetaDescriptionViewlet, MikanMetaRobotViewlet, MikanMetaAutherViewlet,\
	MikanLinkViewlet, MikanCSSViewlet, MikanJSViewlet, MikanLoadedPageViewlet,\
	MikanMetaCompatibleViewlet, MikanMetaViewportViewlet

# ------------------------------------------------------------
## 変数
# ------------------------------------------------------------

# ------------------------------------------------------------
## 関数
# ------------------------------------------------------------

# ------------------------------------------------------------
## クラス
# ------------------------------------------------------------

# ------------------------------------------------------------
class AbstractMikanView(AbstractMikanPerform):

	def is_deflate(self):
		return True

	def __init__(self, r):
		super(AbstractMikanView, self).__init__(r)

	def get_content_type(self):
		return "text/plain"

	def get_content_length(self):
		return None

	def get_content(self):
		return ""

	def is_encode(self):
		return False

	# ----------------------------------------------------
	content = ESiPCProperty(get_content, None, None)
	contenttype = ESiPCProperty(get_content_type, None, None)
	contentlength = ESiPCProperty(get_content_length, None, None)
	isencode = ESiPCProperty(is_encode, None, None)
	isdeflate = ESiPCProperty(is_deflate, None, None)

class MikanBinaryView(AbstractMikanView):

	def is_deflate(self):
		return False

	def __init__(self, r):
		super(MikanBinaryView, self).__init__(r)

		self.FContentType = "application/octet-stream"
		self.FContent = None
		self.FContentLength = 0

	def set_content_type(self, value):
		self.FContentType = value

	def get_content_type(self):
		return self.FContentType

	def set_content_length(self, value):
		self.FContentLength = value

	def get_content_length(self):
		return self.FContentLength

	def set_content(self, value):
		self.FContent = value

	def get_content(self):
		return self.FContent

	# ----------------------------------------------------
	## Property
	content = ESiPCProperty(get_content, set_content, None)
	contenttype = ESiPCProperty(get_content_type, set_content_type, None)
	contentlength = ESiPCProperty(get_content_length, set_content_length, None)

class AbstractMikanFileView(AbstractMikanView):

	def is_deflate(self):
		return False

	def get_filename(self):
		return None

	def set_content_type(self, value):
		self.FContentType = value

	def __init__(self, r):
		super(AbstractMikanFileView, self).__init__(r)

		self.FContentType = None

	def get_content_type(self):
		# 指定あり
		if self.FContentType != None:
			return self.FContentType

		# 自動
		try:
			ct = mimetypes.guess_type(self.filename)[0]
			if ct != None:
				return ct
		except:
			pass

		# デフォルト
		return "text/plain"

	def get_content_length(self):
		return -1

	def perform(self):
		# HTTPヘッダーに設定
		self.request.headers[MIKAN_HTTP_HEADER.NAME_CONTENT_DISPOSITION] = str(MIKAN_HTTP_HEADER.VALUE_CONTENT_DISPOSITION % (self.filename))

		return super(AbstractMikanFileView, self).perform()

	# ----------------------------------------------------
	## Property
	filename = ESiPCProperty(get_filename, None, None)
	contenttype = ESiPCProperty(get_content_type, set_content_type, None)

class MikanFileView(AbstractMikanFileView):

	def get_filename(self):
		return self.FFilename

	def get_filepath(self):
		return self.FFilepath

	def get_content_length(self):
		return os.path.getsize(self.filepath)

	def __init__(self, r, filepath, filename=None):
		super(MikanFileView, self).__init__(r)

		self.FFilepath = filepath
		if filename:
			self.FFilename = filename
		else:
			self.FFilename = os.path.basename(self.filepath)
		self.FContent = None

	def get_content(self):
		if self.FContent == None:
			f = None
			try:
				self.logger.debug(self.filepath)
				f = open(self.filepath, 'rb')
				self.FContent = f.read()
			except:
				self.logger.debug(traceback.format_exc())
				self.FContent = ""
			finally:
				try:
					if f:
						f.close()
				except:
					pass
		return self.FContent

	# ----------------------------------------------------
	## Property
	filepath = ESiPCProperty(get_filepath, None, None)

class MikanFileStorageView(AbstractMikanFileView):

	def get_filename(self):
		return self.FFilename

	def get_subpath(self):
		return self.FSubpath

	def get_name(self):
		return self.FName

	def get_filestorage(self):
		return self.FFileStorage

	def __init__(self, r, filename, filestorage, subpath, name):
		super(MikanFileStorageView, self).__init__(r)

		self.FFileStorage = filestorage
		self.FFilename = filename
		self.FSubpath = subpath
		self.FName = name
		self.FContent = None

	def get_content_length(self):
		return self.filestorage.size(self.subpath, self.name)

	def get_content(self):
		if self.FContent == None:
			try:
				f = self.filestorage.read(self.subpath, self.name)
				self.FContent = f.read()
				f.close()
			except:
				self.logger.debug(traceback.format_exc())
				self.FContent = ""
		return self.FContent

	# ----------------------------------------------------
	## Property
	subpath = ESiPCProperty(get_subpath, None, None)
	name = ESiPCProperty(get_name, None, None)
	filestorage = ESiPCProperty(get_filestorage, None, None)

class MikanTextView(AbstractMikanView):

	def get_content_length(self):
		return len(self.content)

	def __init__(self, r):
		super(MikanTextView, self).__init__(r)

	def get_encode(self):
		return self.request.encode

	def is_encode(self):
		return True

	# ----------------------------------------------------
	## Property
	encode = ESiPCProperty(get_encode, None, None)

class MikanJSONView(MikanTextView):
	def is_deflate(self):
		return False

	def __init__(self, r):
		super(MikanJSONView, self).__init__(r)

	def get_content_type(self):
		return "application/json; charset=%s" % self.encode

	def set_result(self, value):
		self.FResult = value

	def get_result(self):
		return self.FResult

	def get_content(self):
		if (self.result == None):
			return u""
		return json.dumps(self.result, ensure_ascii=False, indent=2, separators=(',',':')).encode("utf-8")

	# ----------------------------------------------------
	## Property
	result = ESiPCProperty(get_result, set_result, None)

class MikanHTMLView(MikanTextView):
	def __init__(self, r):
		super(MikanHTMLView, self).__init__(r)

	def get_content_type(self):
		return "text/html; charset=%s" % self.encode

class MikanXMLView(MikanTextView):

	dtd = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'

	def __init__(self, r):
		self.FXML = None

		super(MikanXMLView, self).__init__(r)

	def get_xml_header(self):
		return '<?xml version="1.0" encoding="%s"?>' % self.encode

	def get_dtd(self):
		return self.dtd

	def set_xml(self, value):
		self.FXML = value

	def get_xml(self):
		return self.FXML

	def get_content_type(self):
		return "text/xml; charset=" + self.encode

	def get_content(self):
		c = self.xmlheader + "\n\n"
		if self.dtd <> None:
			c = c + self.dtd + "\n\n"
		if self.FXML <> None:
#			c.append(self.FXML.documentElement.toprettyxml('', newl='', encoding=self.encode))
			c = c + str_node(self.FXML.documentElement, False) + "\n\n"

		return c.encode("utf-8")

	# ----------------------------------------------------
	## Property
	xml = ESiPCProperty(get_xml, set_xml, None)
	dtd = ESiPCProperty(get_dtd, None, None)
	xmlheader = ESiPCProperty(get_xml_header, None, None)

class MikanXHTMLView(MikanXMLView):

	dtd = '<!DOCTYPE html>'

	def get_dtd(self):
		return self.dtd

	def get_html(self):
		return self.FHtml

	def get_head(self):
		return self.FHead

	def get_title(self):
		return self.FTitle

	def get_robot(self):
		return self.FRobot

	def get_description(self):
		return self.FDescription

	def get_auther(self):
		return self.FAuther

	def get_keywords(self):
		return self.FKeywords

	def get_compatible(self):
		return self.FCompatible

	def get_viewport(self):
		return self.FViewport

	def perform(self):
		return self.html.perform()

	def get_content_type(self):
		return "text/html"

	def get_content(self):
		return super(MikanXHTMLView, self).get_content()

	def __init__(self, r):
		super(MikanXHTMLView, self).__init__(r)

		self.fcontent = None
		self.FHtml = MikanHTMLViewlet(self)
		self.FHead = MikanHeadViewlet(self.html)
		MikanMetaContentTypeViewlet(self.head)
		MikanMetaContentLanguageViewlet(self.head)
		MikanMetaContentStyleTypeViewlet(self.head)
		self.FTitle = MikanTitleViewlet(self.head, "")
		self.FKeywords = MikanMetaKeywordsViewlet(self.head, r.get_config_value("site", "keywords"))
		self.FDescription = MikanMetaDescriptionViewlet(self.head, r.get_config_value("site", "description"))
		self.FRobot = MikanMetaRobotViewlet(self.head, r.get_config_value("site", "robots"))
		self.FAuther = MikanMetaAutherViewlet(self.head, self.request.siteauther)
		self.FCompatible = MikanMetaCompatibleViewlet(self.head, self.request.sitecompatible)
		self.FViewport = MikanMetaViewportViewlet(self.head, self.request.siteviewport)
		MikanMetaViewlet(self.head, "framework-name", MikanXHTMLView.MIKAN_NAME)
		MikanMetaViewlet(self.head, "framework-version", MikanXHTMLView.MIKAN_VERSION)
		MikanMetaViewlet(self.head, "system-name", self.request.sitesystem)
		MikanMetaViewlet(self.head, "system-version", self.request.siteversion)

	# ----------------------------------------------------
	## Property
	html = ESiPCProperty(get_html, None, None)
	head = ESiPCProperty(get_head, None, None)
	title = ESiPCProperty(get_title, None, None)
	auther = ESiPCProperty(get_auther, None, None)
	robot = ESiPCProperty(get_robot, None, None)
	title = ESiPCProperty(get_title, None, None)
	keywords = ESiPCProperty(get_keywords, None, None)
	description = ESiPCProperty(get_description, None, None)
	compatible = ESiPCProperty(get_compatible, None, None)
	viewport = ESiPCProperty(get_viewport, None, None)



class MikanTemplateXHTMLView(MikanXHTMLView):

	def get_css(self):
		return self.FCss

	def get_theme(self):
		return self.FTheme

	def get_js(self):
		return self.FJs

	def get_js_loaded(self):
		return self.FJsLoaded

	def get_favicon(self):
		return self.FFavicon

	def get_loaded_page(self):
		return self.FLoadedPage

	def __init__(self, r):
		super(MikanTemplateXHTMLView, self).__init__(r)

		self.FFavicon = MikanLinkViewlet(self.head, "shortcut icon", r.htdocsurl + "/favicon.ico", "image/x-icon")
		self.favicon.sortkey = 20

		self.FTheme = r.theme
		self.FCss = r.csss
		self.FJs = r.jss
		self.FJsLoaded = r.jsloadeds
		self.FLoadedPage = []

	def is_first_theme(self):
		return False

	def get_theme_path(self):
		return "theme/"

	def perform(self):
		if (self.is_first_theme()) and (self.theme != "-"):
			self.add_css(self.get_theme_path() + self.theme)
		self.add_csss(self.css)
		if (not self.is_first_theme()) and (self.theme != "-"):
			self.add_css(self.get_theme_path() + self.theme)

		self.add_jss(self.js)

		self.add_jss(self.jsloaded, 1000000, self.body)

		MikanLoadedPageViewlet(self.body)

		return super(MikanTemplateXHTMLView, self).perform()

	def add_css(self, css, sortkey=0, parent=None):
		p = parent
		if p == None:
			p = self.head

		css = MikanCSSViewlet(p, u"%s%s" % (self.request.cssurl, css))
		if sortkey != 0:
			css.sortkey = sortkey

		return css

	def add_csss(self, csss, sortkey=0, parent=None):
		p = parent
		if p == None:
			p = self.head

		sk = sortkey
		for c in csss:
			self.add_css(c, sk, p)
			if sk != 0:
				sk = sk + 1

	def add_js(self, js, sortkey=0, parent=None):
		p = parent
		if p == None:
			p = self.head

		js = MikanJSViewlet(p, u"%s%s" % (self.request.jsurl, js))
		if sortkey != 0:
			js.sortkey = sortkey

		return js

	def add_jss(self, jss, sortkey=0, parent=None):
		if jss == None:
			return

		p = parent
		if p == None:
			p = self.head

		sk = sortkey
		for j in jss:
			self.add_js(j, sk, p)
			if sk != 0:
				sk = sk + 1

	# ----------------------------------------------------
	## Property
	css = ESiPCProperty(get_css, None, None)
	theme = ESiPCProperty(get_theme, None, None)
	js = ESiPCProperty(get_js, None, None)
	jsloaded = ESiPCProperty(get_js_loaded, None, None)
	favicon = ESiPCProperty(get_favicon, None, None)
	loadedpage = ESiPCProperty(get_loaded_page, None, None)

class MikanXSLTView(MikanXMLView):

	def __init__(self, r):
		super(MikanXSLTView, self).__init__(r)

		self.FSource = None
		self.FXSLT = None
		self.FParams = None

	def set_params(self, value):
		self.FParams = value

	def get_params(self):
		return self.FParams

	def set_source(self, value):
		self.FSource  = value

	def get_source(self):
		return self.FSource

	def set_xslt(self, value):
		self.FXSLT  = value

	def get_xslt(self):
		return self.FXSLT

	def perform(self):
		xml = libxml2.parseFile(self.fsource)
		self.FXML = self.xslt.applyStylesheet(xml, self.params)

		return True

	def get_content_type(self):
		return "Content-type: text/html; charset=" + self.encode

	def get_content(self):
		return self.xml.__str__()

	# ----------------------------------------------------
	## Property
	params = ESiPCProperty(get_params, set_params, None)
	xslt = ESiPCProperty(get_xslt, set_source, None)
	source = ESiPCProperty(get_source, set_source, None)

class MikanSXBView(MikanXSLTView):

	def __init__(self, r):
		super(MikanSXBView, self).__init__(r)

		xslt_name = self.request.mikanroot + '/sxmlbook/xslt/xhtml/sxmlbook.xsl'
#		sys.stderr.write(xslt_name + '\n')
		styleDoc = libxml2.parseFile(xslt_name)
		self.xslt = libxslt.parseStylesheetDoc(styleDoc)
		self.params = { "html.cdir": "'" + self.request.cssurl + "'" }

# ------------------------------------------------------------
## メイン関数
# ------------------------------------------------------------
## メイン処理
if __name__ == '__main__':
	pass

# ------------------------------------------------------------
